package com.shsc.seasar.search.query;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.regex.Pattern;

import org.eclipse.core.internal.resources.File;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.search.core.text.TextSearchEngine;
import org.eclipse.search.core.text.TextSearchMatchAccess;
import org.eclipse.search.core.text.TextSearchRequestor;
import org.eclipse.search.core.text.TextSearchScope;
import org.eclipse.search.internal.core.text.PatternConstructor;
import org.eclipse.search.internal.ui.text.FileMatch;
import org.eclipse.search.internal.ui.text.FileSearchQuery;
import org.eclipse.search.internal.ui.text.LineElement;
import org.eclipse.search.ui.ISearchPageContainer;
import org.eclipse.search.ui.ISearchQuery;
import org.eclipse.search.ui.ISearchResult;
import org.eclipse.search.ui.text.AbstractTextSearchResult;
import org.eclipse.search.ui.text.FileTextSearchScope;
import org.eclipse.search.ui.text.Match;

import com.shsc.seasar.search.util.TableSearchEnum;

public class SqlSearchQuery extends FileSearchQuery implements ISearchQuery {
	
	private String colName;
	
	private String tblName;
	
	private boolean isRegEx;
	
	private FileTextSearchScope fScope;
	
	private int searchPageContainer;
	
	private TableSearchEnum searchPattan;
	
	public SqlSearchQuery(String _searchText, boolean _isRegEx,
			boolean _isCaseSensitive, TextSearchScope _scope, String _columnName,
			int _searchPageContainer, TableSearchEnum _searchPattan) {
		super(_searchText, false, _isCaseSensitive, (FileTextSearchScope)_scope);
		tblName = _searchText;
		isRegEx = _isRegEx;
		colName = _columnName;
		fScope = (FileTextSearchScope)_scope;
		searchPageContainer = _searchPageContainer;
		searchPattan = _searchPattan;
	}

	public boolean canRerun() {
		return super.canRerun();
	}

	public boolean canRunInBackground() {
		return super.canRunInBackground();
	}

	public String getLabel() {
		return super.getLabel();
	}

	public ISearchResult getSearchResult() {
		return super.getSearchResult();
	}

	public IStatus run(IProgressMonitor arg0) throws OperationCanceledException {
		
		IStatus s = null;
		if (searchPattan == TableSearchEnum.DELETE) {
			s = super.run(arg0);
			if (colName == null || "".equals(colName)) {
				return s;
			}
			AbstractTextSearchResult textResult = (AbstractTextSearchResult) getSearchResult();
			
			Pattern searchPattern= PatternConstructor.createPattern(colName, false, isRegEx);
			
			TextSearchResultCollector collector= new TextSearchResultCollector(textResult, isFileNameSearch(), false);
			Object[] result = textResult.getElements();
			textResult.removeAll();
			s = TextSearchEngine.create().search(createTextSearchScope(getDeleteExtensions(result)), collector, searchPattern, arg0);
		} else if (searchPattan == TableSearchEnum.ADD) {
			AbstractTextSearchResult textResult= (AbstractTextSearchResult) getSearchResult();
			textResult.removeAll();
			Pattern searchPattern= PatternConstructor.createPattern(tblName, false, false);
			TextSearchResultCollector collector= new TextSearchResultCollector(textResult, isFileNameSearch(), false);
			s = TextSearchEngine.create().search(createTextSearchScope(getAddSqlExtensions()), collector, searchPattern, arg0);
			
			String daoName = tblName.replace("_", "") + "Dao";
			searchPattern= PatternConstructor.createPattern(daoName, false, false);
			collector= new TextSearchResultCollector(textResult, isFileNameSearch(), false);
			s = TextSearchEngine.create().search(createTextSearchScope(getAddDaoExtensions()), collector, searchPattern, arg0);
			textResult= (AbstractTextSearchResult) getSearchResult();
			for (Object element : textResult.getElements()) {
				File f = (File)element;
				if (f.getName().indexOf(".java") < 0) {
					continue;
				}
				Match[] matchs = textResult.getMatches(element);
				for (Match match : matchs) {
					//match
				}
			}
		}
		return s;
	}
	
	private FileTextSearchScope createTextSearchScope(String[] result) {
		switch (searchPageContainer) {
			case ISearchPageContainer.WORKSPACE_SCOPE:
				return FileTextSearchScope.newWorkspaceScope(result, false);
			case ISearchPageContainer.SELECTION_SCOPE:
				return FileTextSearchScope.newSearchScope(fScope.getRoots(), result, false);
			case ISearchPageContainer.SELECTED_PROJECTS_SCOPE:
				return FileTextSearchScope.newSearchScope(fScope.getRoots(), result, false);
			case ISearchPageContainer.WORKING_SET_SCOPE:
				return FileTextSearchScope.newSearchScope(fScope.getWorkingSets(), result, false);
			default:
				return FileTextSearchScope.newWorkspaceScope(result, false);
		}
    }
	
	private String[] getAddDaoExtensions() {
		ArrayList<String> exts = new ArrayList<String>();
        String[] dottedValidSourceFiles = new String[] {"impl.java"};
        for (String sourceFile : dottedValidSourceFiles) {
            exts.add('*'+sourceFile);
        }
        return exts.toArray(new String[0]);
	}
	
	private String[] getAddSqlExtensions() {
		ArrayList<String> exts = new ArrayList<String>();
        String[] dottedValidSourceFiles = new String[] {"_insert*.sql", "_add*.sql"};
        for (String sourceFile : dottedValidSourceFiles) {
            exts.add('*'+sourceFile);
        }
        return exts.toArray(new String[0]);
	}
	
	private String[] getDeleteExtensions(Object[] result) {
		ArrayList<String> exts = new ArrayList<String>();
		for (Object obj : result) {
			File f = (File)obj;
			String path = f.getName();
			int lastIndex = path.lastIndexOf("/");
			if (lastIndex > 0) {
				path = path.substring(lastIndex);
			}
//			if (checkDaoMethodExists(f)) {
				exts.add(path);
//			}
		}
        return exts.toArray(new String[0]);
	}
	
	private boolean checkDaoMethodExists(File f) {
		Class cls = getClassObj(f.getProjectRelativePath(), f.getName());
		if (cls == null) {
			return false;
		}
		String methodName = f.getName().substring(f.getName().indexOf("Dao_") + 4);
		methodName = methodName.substring(0, methodName.length() - 4);
		try {
			Method method = cls.getMethod(methodName);
			return true;
		} catch (SecurityException e) {
			e.printStackTrace();
		} catch (NoSuchMethodException e) {
			e.printStackTrace();
		}
		return false;
	}
	
	private Class getClassObj(IPath path, String sqlName) {
		String daoName = sqlName.substring(0, sqlName.indexOf("Dao") + 3);
		
		for (int i = 0; i < path.segmentCount() - 1; i++) {
//			String className = getClassName(path, i, daoName);
//			Class tmp;
//			try {
//				tmp = Class.forName(className);				
//			} catch (ClassNotFoundException e) {
//				try {
//					java.io.File file = new java.io.File("c:\\1.log");
//					//FileOutputStream
//					if (!file.exists()) {
//						file.createNewFile();					
//					}
//					FileOutputStream stream = new FileOutputStream(file);
//					BufferedWriter   rd   =   new BufferedWriter(new OutputStreamWriter(stream,"utf-8"));
//					rd.write(className);
//					rd.write(e.getMessage());
//					rd.close();
//					stream.close();
//					
//				} catch (Exception ex) {
//					throw ex;
////					ex.printStackTrace();
//				}
//				e.printStackTrace();
//				continue;
//			}
//			if (tmp != null) {
//				return tmp;
//			}
		}
		return null;
	}
	
	private String getClassName(IPath path, int startIndex, String daoName) {
		StringBuffer retStr = new StringBuffer();
		for (int i = startIndex; i < path.segmentCount() - 1; i++) {
			retStr.append(path.segment(i));
			retStr.append(".");
		}
		retStr.append(daoName);
		return retStr.toString();
	}
	
	
	private final static class TextSearchResultCollector extends TextSearchRequestor {

		private final AbstractTextSearchResult fResult;
		private final boolean fIsFileSearchOnly;
		private final boolean fSearchInBinaries;
		private ArrayList fCachedMatches;

		private TextSearchResultCollector(AbstractTextSearchResult result, boolean isFileSearchOnly, boolean searchInBinaries) {
			fResult= result;
			fIsFileSearchOnly= isFileSearchOnly;
			fSearchInBinaries= searchInBinaries;

		}

		public boolean acceptFile(IFile file) throws CoreException {
			if (fIsFileSearchOnly) {
				fResult.addMatch(new FileMatch(file));
			}
			flushMatches();
			return true;
		}

		/* (non-Javadoc)
		 * @see org.eclipse.search.core.text.TextSearchRequestor#reportBinaryFile(org.eclipse.core.resources.IFile)
		 */
		public boolean reportBinaryFile(IFile file) {
			return fSearchInBinaries;
		}

		public boolean acceptPatternMatch(TextSearchMatchAccess matchRequestor) throws CoreException {
			int matchOffset= matchRequestor.getMatchOffset();

			LineElement lineElement= getLineElement(matchOffset, matchRequestor);
			if (lineElement != null) {
				FileMatch fileMatch= new FileMatch(matchRequestor.getFile(), matchOffset, matchRequestor.getMatchLength(), lineElement);
				fCachedMatches.add(fileMatch);
			}
			return true;
		}

		private LineElement getLineElement(int offset, TextSearchMatchAccess matchRequestor) {
			int lineNumber= 1;
			int lineStart= 0;
			if (!fCachedMatches.isEmpty()) {
				// match on same line as last?
				FileMatch last= (FileMatch) fCachedMatches.get(fCachedMatches.size() - 1);
				LineElement lineElement= last.getLineElement();
				if (lineElement.contains(offset)) {
					return lineElement;
				}
				// start with the offset and line information from the last match
				lineStart= lineElement.getOffset() + lineElement.getLength();
				lineNumber= lineElement.getLine() + 1;
			}
			if (offset < lineStart) {
				return null; // offset before the last line
			}

			int i= lineStart;
			int contentLength= matchRequestor.getFileContentLength();
			while (i < contentLength) {
				char ch= matchRequestor.getFileContentChar(i++);
				if (ch == '\n' || ch == '\r') {
					if (ch == '\r' && i < contentLength && matchRequestor.getFileContentChar(i) == '\n') {
						i++;
					}
					if (offset < i) {
						String lineContent= getContents(matchRequestor, lineStart, i); // include line delimiter
						return new LineElement(matchRequestor.getFile(), lineNumber, lineStart, lineContent);
					}
					lineNumber++;
					lineStart= i;
				}
			}
			if (offset < i) {
				String lineContent= getContents(matchRequestor, lineStart, i); // until end of file
				return new LineElement(matchRequestor.getFile(), lineNumber, lineStart, lineContent);
			}
			return null; // offset outside of range
		}

		private static String getContents(TextSearchMatchAccess matchRequestor, int start, int end) {
			StringBuffer buf= new StringBuffer();
			for (int i= start; i < end; i++) {
				char ch= matchRequestor.getFileContentChar(i);
				if (Character.isWhitespace(ch) || Character.isISOControl(ch)) {
					buf.append(' ');
				} else {
					buf.append(ch);
				}
			}
			return buf.toString();
		}

		public void beginReporting() {
			fCachedMatches= new ArrayList();
		}

		public void endReporting() {
			flushMatches();
			fCachedMatches= null;
		}

		private void flushMatches() {
			if (!fCachedMatches.isEmpty()) {
				fResult.addMatches((Match[]) fCachedMatches.toArray(new Match[fCachedMatches.size()]));
				fCachedMatches.clear();
			}
		}
	}

}
